package top.ibase4j.core.support.jedis;

import java.io.Serializable;
import java.util.Set;

import org.apache.commons.pool2.impl.GenericObjectPoolConfig;

import com.alibaba.fastjson.JSON;

import redis.clients.jedis.BinaryJedisCluster;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisClusterCommand;
import top.ibase4j.core.Constants;
import top.ibase4j.core.support.cache.ICacheManager;
import top.ibase4j.core.util.InstanceUtil;
import top.ibase4j.core.util.PropertiesUtil;

/**
 * Redis集群
 * @author ShenHuaJie
 * @version 2018年3月8日 下午5:24:07
 */
public class JedisClusterTemplate extends BinaryJedisCluster implements ICacheManager {
    private Integer EXPIRE = PropertiesUtil.getInt("redis.expiration");

    public JedisClusterTemplate(Set<HostAndPort> jedisClusterNode, int timeout, int maxAttempts,
        GenericObjectPoolConfig poolConfig) {
        super(jedisClusterNode, timeout, maxAttempts, poolConfig);
    }

    public JedisClusterTemplate(Set<HostAndPort> jedisClusterNode, int connectionTimeout, int soTimeout,
        int maxAttempts, GenericObjectPoolConfig poolConfig) {
        super(jedisClusterNode, connectionTimeout, soTimeout, maxAttempts, poolConfig);
    }

    public JedisClusterTemplate(Set<HostAndPort> jedisClusterNode, int connectionTimeout, int soTimeout,
        int maxAttempts, String password, GenericObjectPoolConfig poolConfig) {
        super(jedisClusterNode, connectionTimeout, soTimeout, maxAttempts, password, poolConfig);
    }

    public JedisClusterTemplate(Set<HostAndPort> nodes, int timeout) {
        super(nodes, timeout);
    }

    public JedisClusterTemplate(Set<HostAndPort> nodes) {
        super(nodes);
    }

    interface Executor<K> {
        public K execute(Jedis jedis);
    }

    public <K> K run(String key, Executor<K> executor, Integer... expire) {
        return new JedisClusterCommand<K>(connectionHandler, maxAttempts) {
            public K execute(Jedis jedis) {
                K result = executor.execute(jedis);
                if (jedis.exists(key)) {
                    if ((expire == null || expire.length == 0) && !key.startsWith(Constants.SYSTEM_CACHE_NAMESPACE)) {
                        jedis.expire(key, EXPIRE);
                    } else if (expire != null && expire.length == 1) {
                        jedis.expire(key, expire[0]);
                    }
                }
                return result;
            }
        }.run(key);
    }

    public final Object get(final String key) {
        return run(key, new Executor<Object>() {
            public Object execute(Jedis jedis) {
                return JSON.parse(jedis.get(key));
            }
        });
    }

    public final Object get(final String key, final Integer expire) {
        return run(key, new Executor<Object>() {
            public Object execute(Jedis jedis) {
                return JSON.parse(jedis.get(key));
            }
        }, expire);
    }

    public void set(final String key, final Serializable value) {
        run(key, new Executor<String>() {
            public String execute(Jedis jedis) {
                return jedis.set(key, JSON.toJSONString(value));
            }
        });
    }

    public final void set(final String key, final Serializable value, final int seconds) {
        run(key, new Executor<String>() {
            public String execute(Jedis jedis) {
                return jedis.setex(key, seconds, JSON.toJSONString(value));
            }
        }, seconds, seconds);
    }

    public final Boolean exists(final String key) {
        return run((String)key, new Executor<Boolean>() {
            public Boolean execute(Jedis jedis) {
                return jedis.exists((String)key);
            }
        });
    }

    public final void del(final String key) {
        run((String)key, new Executor<Long>() {
            public Long execute(Jedis jedis) {
                return jedis.del((String)key);
            }
        });
    }

    public final String type(final String key) {
        return run((String)key, new Executor<String>() {
            public String execute(Jedis jedis) {
                return jedis.type((String)key);
            }
        });
    }

    /**
     * 在某段时间后失效
     * 
     * @return
     */
    public final Boolean expire(final String key, final int seconds) {
        return run(key, new Executor<Long>() {
            public Long execute(Jedis jedis) {
                return jedis.expire(key, seconds);
            }
        }, seconds, seconds) == 1;
    }

    public final Boolean expireAt(final String key, final long unixTime) {
        return run(key, new Executor<Long>() {
            public Long execute(Jedis jedis) {
                return jedis.expireAt(key, unixTime);
            }
        }) == 1;
    }

    public final Long ttl(final String key) {
        return run(key, new Executor<Long>() {
            public Long execute(Jedis jedis) {
                return jedis.ttl(key);
            }
        });
    }

    public final void setrange(final String key, final long offset, final String value) {
        run(key, new Executor<Long>() {
            public Long execute(Jedis jedis) {
                return jedis.setrange(key, offset, value);
            }
        });
    }

    public final String getrange(final String key, final long startOffset, final long endOffset) {
        return run(key, new Executor<String>() {
            public String execute(Jedis jedis) {
                return jedis.getrange(key, startOffset, endOffset);
            }
        });
    }

    public final Object getSet(final String key, final Serializable value) {
        return run(key, new Executor<Object>() {
            public Object execute(Jedis jedis) {
                return JSON.parse(jedis.getSet(key, JSON.toJSONString(value)));
            }
        });
    }

    public final Long incr(final String key) {
        return run(key, new Executor<Long>() {
            public Long execute(Jedis jedis) {
                return jedis.incr(key);
            }
        });
    }

    public final void hset(final String key, final Serializable field, final Serializable value) {
        run(key, new Executor<Object>() {
            public Object execute(Jedis jedis) {
                return jedis.hset(key, JSON.toJSONString(field), JSON.toJSONString(value));
            }
        });
    }

    public final Object hget(final String key, final Serializable field) {
        return run(key, new Executor<Object>() {
            public Object execute(Jedis jedis) {
                return JSON.parse(jedis.hget(key, JSON.toJSONString(field)));
            }
        });
    }

    public final void hdel(final String key, final Serializable field) {
        run(key, new Executor<Long>() {
            public Long execute(Jedis jedis) {
                return jedis.hdel(key, JSON.toJSONString(field));
            }
        });
    }

    public Set<Object> getAll(String pattern) {
        return run(pattern, new Executor<Set<Object>>() {
            public Set<Object> execute(Jedis jedis) {
                Set<String> keys = jedis.hkeys(pattern);
                Set<Object> set = InstanceUtil.newHashSet();
                for (String key : keys) {
                    set.add(JSON.parse(jedis.get(key)));
                }
                return set;
            }
        });
    }

    public void delAll(String pattern) {
        run(pattern, new Executor<Object>() {
            public Object execute(Jedis jedis) {
                Set<String> keys = jedis.hkeys(pattern);
                for (String key : keys) {
                    jedis.del(key);
                }
                return true;
            }
        });
    }

    public boolean setnx(final String key, final Serializable value) {
        return run(key, new Executor<Long>() {
            public Long execute(Jedis jedis) {
                return jedis.setnx(key, JSON.toJSONString(value));
            }
        }) == 1;
    }

    public void unlock(String key) {
        del(key);
    }

    public boolean getLock(String key) {
        return run(key, new Executor<Long>() {
            public Long execute(Jedis jedis) {
                return jedis.setnx(key, "0");
            }
        }) == 1;
    }

    public boolean lock(String key) {
        return run(key, new Executor<Long>() {
            public Long execute(Jedis jedis) {
                return jedis.setnx(key, "0");
            }
        }) == 1;
    }

    public Object getFire(String key) {
        expireAt(key, EXPIRE);
        return get(key);
    }

    public Set<Object> getAll(String pattern, Integer expire) {
        return run(pattern, new Executor<Set<Object>>() {
            public Set<Object> execute(Jedis jedis) {
                Set<String> keys = jedis.hkeys(pattern);
                Set<Object> set = InstanceUtil.newHashSet();
                for (String key : keys) {
                    set.add(JSON.parse(jedis.get(key)));
                    jedis.expire(key, expire);
                }
                return set;
            }
        });
    }
}
